//
// Created by Moonbreeze on 2015/12/17.
//

#ifndef CUSTOMJVM_JAVACLASS_H
#define CUSTOMJVM_JAVACLASS_H

#include <string>
#include "types.h"
#include "constants.h"

class ClassHeap;
class ObjectHeap;

/*
 * Constant Pool
 * the base struct 
 * the tag value can be like CONSTANT_Integer
 * and we can use struct CONSTANT_Integer_info* to point the address
 * and get the value	
 */
struct CONSTANT_POOL_info {
    u1 tag;
    u1* info;
};
/*
class info struct
and the name_index is a constant_pool index
the value in this index is a CONSTANT_Utf8_info
the value is a fully qualified name like java/lang/String
*/
struct CONSTANT_Class_info{
    u1 tag;
    u2 name_index;
};
/*
Field info
the class_index point to the class this field blongs to
the name_and_type_index is a fully qualified name like
java/lang/String@valueOf(F)Ljava/lang/String
*/
struct CONSTANT_Fieldref_info{
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
};
/*
method info 
same as the field info
*/
struct CONSTANT_Methodref_info{
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
};

struct CONSTANT_InterfaceMethodref_info{
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
};
/*
the string_index value is a index in constant_pool
the data in this index must be a CONSTANT_Utf8_info
*/
struct CONSTANT_String_info{
    u1 tag;
    u2 string_index;
};

struct CONSTANT_Integer_info{
    u1 tag;
    u4 value;
};

struct CONSTANT_Float_info{
    u1 tag;
    u4 values;
};

struct CONSTANT_Long_info{
    u1 tag;
    u4 high_value;
    u4 low_value;
};
struct CONSTANT_Double_info{
    u1 tag;
    u4 high_value;
    u4 low_value;
};
struct CONSTANT_NameAndType_info{
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
};
struct CONSTANT_Utf8_info{
    u1 tag;
    u2 length;
    u1* bytes;
};
/*
 * Attribute
 * */
struct Attribute_info{
    u2 attribute_name_index;
    u4 attribute_length;
    u1* info;
};
/*
 * Class Field
 */
struct Field_info{
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    Attribute_info* attributes;
};

struct Field_info_ex:public Field_info{
    Field_info* pFieldInfoBase;
};

struct Method_info{
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    Attribute_info* attributes;
};
struct ConstantValue_attribute{
    u2 attribute_name_index;
    u4 attribute_length;
    u2 constant_value_index;
};
struct Exception_table{
    u2 start_pc;
    u2 end_pc;
    u2 handler_pc;
    u2 catch_type;
};

struct Code_attribute{
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1* code;
    u2 exception_table_length;
    Exception_table* exception_table;
    u2 attributes_count;
    Attribute_info* attributes;
};

struct Method_info_ex:public Method_info{
    Method_info* pMethodInfoBase;
    Code_attribute* pCode_attr;
};
/*
 * at the beginning of every java class file
 */
struct JavaClassFileFormat{
    /*
     * identify whether it is a java class file
     * the value must be 0xCAFEBABE
     */
    u4 magic;
    /*
     * identify the version of compile jdk
     * major_version start by 45 = java 1.x,
     * so if major_version = (H)33 = 51
     * then the version = 51 - 45 = 6
     * the jdk version = 1 + 6 = 7.x
     */
    u2 minor_version;
    u2 major_version;
    /*
     * a class file may have many constant value
     * so there are two variables, count indicate the length,
     * while the pointer point to the data
     */
    u2 constant_pool_count;
    CONSTANT_POOL_info** constant_pool;
    /*
     * identify the access level of this class
     * like public or private
     */
    u2 access_flags;
    /*
     * identify this class type,the value is a index in constant pool
     * and the object in this index must be a CONSTANT_Class_info struct
     */
    u2 this_class;
    /*
     * same as this_class,but it identify the super_class
     */
    u2 super_class;
    /*
     * same as the constant_pool_count and constant_pool
     * this is the interface the class implement
     * the value in interfaces[] is a index of constant_pool
     * each element in this index must be a CONSTANT_Class_info struct
     */
    u2 interfaces_count;
    u2* interfaces;
    /*
     * same as constant_pool_count
     */
    u2 fields_count;
    Field_info_ex* fields;
    u2 methods_count;
    Method_info_ex* methods;
    /*
     *
     */
    u2 attributes_count;
    Attribute_info** attributes;
};

class JavaClass:public JavaClassFileFormat{
public:
    JavaClass(void);
    virtual ~JavaClass(void);

    /*
     * load the class file into memory
     */
    virtual bool LoadClassFormFile(std::string FilePath);
    void SetByteCode(void* pByteCode);
    bool ParseClass(void);
    bool ParseInterfaces(char* &p);
    bool ParseFields(char* &p);
    bool ParseMethods(char* &p);
    bool ParseAttributes(char* &p);
    bool GetConstantPool(u2 index,CONSTANT_POOL_info& const_pool);

    bool GetStringFormConstPool(int index,std::string& strValue);
    std::string GetName(void);
    std::string GetSuperClassName(void);
    bool ParseMethodCodeAttribute(int methodIndex,Code_attribute* pCode_attr);
    int GetMethodIndex(std::string methodName,std::string methodDesc,JavaClass* &pClass);
    int GetFieldIndex(std::string strName,std::string& strDesc);

    void SetClassHeap(ClassHeap* pClassHeap){this->m_pClassHeap = pClassHeap;}
    virtual u4 GetObjectSize(void);
    virtual u4 GetObjectFieldCount(void);
    JavaClass* GetSuperClass(void);
    bool CreateObject(u2 index,ObjectHeap* pObjectHeap,Object& object);
    bool CreateObjectArray(u2 index,u4 count,ObjectHeap* pObjectHeap,Object& object);

private:
    size_t m_nByteCodeLength;
    char* m_pByteCode;
    u2 m_nObjectFieldsCount;
    bool ParseConstantPool(char* &p);
    int GetConstantPoolSize(char* p);
    ClassHeap* m_pClassHeap;
};
#endif //CUSTOMJVM_JAVACLASS_H
